package com.weipeep.common.utils.httpclient;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Callable;
import java.util.concurrent.FutureTask;
import java.util.concurrent.TimeUnit;

import okhttp3.Call;
import okhttp3.Callback;
import okhttp3.Cookie;
import okhttp3.CookieJar;
import okhttp3.HttpUrl;
import okhttp3.Interceptor;
import okhttp3.MediaType;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;
import okhttp3.ResponseBody;
import okhttp3.internal.http.RealResponseBody;
import okio.GzipSource;
import okio.Okio;
import com.weipeep.common.utils.httpclient.HttpRequest.Method;
import com.weipeep.common.utils.httpclient.util.IOUtil;
import com.weipeep.common.utils.httpclient.util.UrlUtil;

public class OkHttpClientService implements HttpService {
	OkHttpClient.Builder builder = new OkHttpClient.Builder().cookieJar(new CookieStore()) ;
	OkHttpClient client;
	Request.Builder request ;
	public OkHttpClientService() {
		super();
		bindInterceptor();
		client = builder.build();
	}

	@Override
	public HttpResponse execute(HttpRequest req) throws Exception {
		long start = System.currentTimeMillis();
		try{
			request = new Request.Builder();
			initParams(req);
			getHttpRequestBase(req);
			
			Call call = client.newCall(request.build());
			OkhttpFuture future = new OkhttpFuture().exec(call);
			Response res = future.get(req.getReadTimeout(), TimeUnit.MILLISECONDS);
			HttpResponse response = new HttpResponse(res.code(), res.message());
			for(String name: res.headers().names()){
				response.setHeader(name, res.headers().get(name));
			}
			CookieStore cookieStore = (CookieStore) client.cookieJar();
			for(Cookie cookie: cookieStore.getCookieStore()){
				response.setCookie(cookie.name(), cookie.value());
			}
			
			response.setResponseData(res.body().source().readByteArray()); // 解决conten-length =0 的问题
			return response;
		}finally{
			long l = (System.currentTimeMillis() - start);
			String body = req.toString(req.getPostParams());
			System.out.println(l+"::URL::" +req.getUrl()+"::"+body);
		}
		
	}

	private void initParams(HttpRequest req) {
		builder.connectTimeout(req.getConnectTimeout(), TimeUnit.MILLISECONDS).readTimeout(req.getReadTimeout(), TimeUnit.MILLISECONDS);
	}

	@Override
	public void bindInterceptor() {
		builder.addInterceptor(new Interceptor() {

			@Override
			public Response intercept(Chain chain) throws IOException {
				Request request = chain.request();
				if (request.header("Accept-Encoding")!=null) {
					request = request.newBuilder().addHeader("Accept-Encoding", "gzip").build();
				}
				Response response = chain.proceed(request);
				if("gzip".equals(response.header("Content-Encoding"))){
					GzipSource responseBody = new GzipSource(response.body().source());
					return response.newBuilder().body(new RealResponseBody(response.header("Content-Type"), 0, Okio.buffer(responseBody)))
			        .build();
				}

				return response;
			}
		});

	}

	

	@Override
	public Object getHttpRequestBase(HttpRequest req) {
		if (req.getMethod() == Method.post) {
			request.url(req.getUrl());
			if (req.getPostParams().size() > 0) {
				parsePostParams(req);
			}else if (req.getFileMap().size() > 0) {
				parseMartipart(req);
			}else if(req.getRequestBody() != null) {
				request.post(RequestBody.create(MediaType.parse(req.getHeaders().get("Content-Type")), req.getRequestBody()));
			}
		} else {
			String url = req.getUrl();
			if (req.getPostParams().size() > 0) {
				String spe = req.getUrl().indexOf("?") != -1 ? "&" : "?";
				url += spe + req.postParamsToString();
			}
			request.url(url);
		}

		parseHeader(req);
		parseCookies(req);

		return request;
	}

	@Override
	public void parseHeader(HttpRequest req) {
		Map<String, String> headers = req.getHeaders();
		for (String key : headers.keySet()) {
			request.addHeader(key, headers.get(key));
		}
	}
	
	@Override
	public void removeAllCookie() {
		CookieStore cookieStore = (CookieStore) client.cookieJar();
		cookieStore.getCookieStore().clear();
	}
	
	@Override
	public void removeCookie(String name) {
		CookieStore cookieStore = (CookieStore) client.cookieJar();
		Iterator<Cookie> ite = cookieStore.getCookieStore().iterator();
		 while(ite.hasNext()){
			 if(ite.next().name().equals(name)){
				 ite.remove();
			 }
		 }
	}

	@Override
	public void parseCookies(HttpRequest req) {
		CookieStore cookieStore = (CookieStore) client.cookieJar();
		String domain = UrlUtil.getDomain(req.getUrl());
		
		for (String key : req.getCookies().keySet()) {
			Cookie cookie = new Cookie.Builder().path("/").domain(domain)
					.name(key).value(req.getCookies().get(key)).build();
			cookieStore.getCookieStore().add(cookie);
		}
	}

	@Override
	public void parsePostParams(HttpRequest req) {
		RequestBody body = RequestBody.create(MediaType.parse("application/x-www-form-urlencoded"), req.postParamsToString());
		request.post(body);
	}

	@Override
	public void parseMartipart(HttpRequest req) {
		ByteArrayOutputStream out = new ByteArrayOutputStream();
		try {
			for(String key: req.getFileMap().keySet()){
				File f = req.getFileMap().get(key);
				out.write(IOUtil.toFormData(key, f));
			}
			out.write(IOUtil.writeBoundary(false));
		} catch (IOException e) {
			e.printStackTrace();
		}
		RequestBody body = RequestBody.create(MediaType.parse("multipart/form-data; boundary="+IOUtil.BOUNDARY),out.toByteArray() );
		request.post(body);
	}

	@Override
	public void shutdown() {
		// TODO Auto-generated method stub

	}

	@Override
	public boolean isShutdown() {
		// TODO Auto-generated method stub
		return false;
	}

	static class CookieStore implements CookieJar {

		private final List<Cookie> cookieStore = new ArrayList<>();

		@Override
		public void saveFromResponse(HttpUrl url, List<Cookie> cookies) {
			cookieStore.addAll(cookies);
		}

		@Override
		public List<Cookie> loadForRequest(HttpUrl url) {
			return cookieStore;
		}

		public List<Cookie> getCookieStore() {
			return cookieStore;
		}

	}

	public static class OkhttpFuture extends FutureTask<Response> {

		public OkhttpFuture(Callable<Response> callable) {
			super(callable);
		}

		public OkhttpFuture() {
			super(new Thread(), null);
		}

		private OkhttpFuture exec(Call call) {
			call.enqueue(new Callback() {

				@Override
				public void onFailure(Call call, IOException e) {
				}

				@Override
				public void onResponse(Call call, Response res) throws IOException {
					OkhttpFuture.this.set(res); // set 后可以直接释放 future.get()
				}
			});
			return this;
		}
	}

}
